import { removeArr, on } from "./util";

function parseQuery2Obj(urlquery) {
  const query = Object.create(null);
  if (!urlquery) return query;
  let urlq = urlquery;
  if (urlq.startsWith("?")) urlq = urlq.substr(1);
  const qs = urlq.split("&");
  qs.forEach((nv) => {
    const dINdex = nv.indexOf("=");
    if (dINdex > -1) {
      query[decodeURIComponent(nv.substr(0, dINdex))] = decodeURIComponent(
        nv.substr(dINdex + 1)
      );
    } else {
      query[nv] = "";
    }
  });
  return query;
}

function parseQueryObj2Str(queryObj) {
  const queryArr = [];
  Object.keys(queryObj).forEach((name) => {
    queryArr.push(
      `${encodeURIComponent(name)}=${encodeURIComponent(queryObj[name])}`
    );
  });
  if (queryArr.length > 0) return `?${queryArr.join("&")}`;
  return "";
}

export class QueryRouter {
  constructor(useCurrUrlPathToBase) {
    if (useCurrUrlPathToBase) {
      const urlpath = window.location.pathname;
      if (urlpath.endsWith("/")) {
        this.baseUrlPath = urlpath.substring(0, urlpath.length - 1);
      } else {
        this.baseUrlPath = urlpath;
      }
    } else {
      this.baseUrlPath = "";
    }
    this.routers = new Map();
  }

  /**
   * 添加路由匹配, 页面已加载也是会触发
   * @param urlRegex 使用 相对路径+查询参数 进行匹配,如:"/a/b.c?d=1&e=2"
   * @param cb (url, query)=>void  query为查询参数的对象化
   */
  onRoute(urlRegex, cb) {
    let cbList = this.routers.get(urlRegex);
    if (!cbList) cbList = [];
    cbList.push(cb);
    this.routers.set(urlRegex, cbList);
  }

  offRoute(urlRegex, cb) {
    const cbList = this.routers.get(urlRegex);
    if (!cbList) return true;
    removeArr(cbList, (c) => c === cb);
    this.routers.set(urlRegex, cbList);
    return true;
  }

  /**
   * 路由到一个地址(地址变更但页面不跳转), 触发对应的路由
   */
  routeUrl(url) {
    window.history.pushState(null, null, this.baseUrlPath + url);
    this.emitRouterUrl(url);
  }

  /**
   * 路由地址不变,但追加或覆盖参数
   */
  routeAppQuery(queryObj) {
    let urlpath = window.location.pathname;
    if (this.baseUrlPath && urlpath.startsWith(this.baseUrlPath)) {
      urlpath = urlpath.substring(this.baseUrlPath.length);
    }
    const urlquery = window.location.search;
    const query = parseQuery2Obj(urlquery);
    const queryMerger = Object.assign(query, queryObj);
    const urlqueryMerger = parseQueryObj2Str(queryMerger);
    this.routeUrl(urlpath + urlqueryMerger);
  }

  emitRouterUrl(url) {
    const r = url.matchAll(/^(http[s]:\/\/[^/]+)?(\/[^?]*)(\?.*)?$/gi);
    if (!r) return false;
    const m = r[0];
    if (!m || m.length !== 4) return false;
    let urlpath = m[2];
    if (this.baseUrlPath && urlpath.startsWith(this.baseUrlPath)) {
      urlpath = urlpath.substring(this.baseUrlPath.length);
    }
    const urlquery = m[3];
    return this.emitRouter(urlpath, urlquery);
  }

  emitRouter(urlpath, urlquery) {
    const url = urlpath + urlquery;
    const query = parseQuery2Obj(urlquery);
    let hasMatch = false;
    this.routers.forEach((routeArr, urlRegex) => {
      if (url.match(urlRegex)) {
        hasMatch = true;
        routeArr.forEach((cb) => {
          cb(url, query);
        });
      }
    });
    return hasMatch;
  }

  /**
   * 初始化路由器(入口等页面加载完,或所有模块加载完,统一调用, 即需要在路由都注册完后调用)
   */
  initRouter() {
    const emitFn = () => {
      let urlpath = window.location.pathname;
      if (this.baseUrlPath && urlpath.startsWith(this.baseUrlPath)) {
        urlpath = urlpath.substring(this.baseUrlPath.length);
      }
      this.emitRouter(urlpath, window.location.search);
    };
    on(window, ["popstate"], emitFn);
    // 初始触发一次
    emitFn();
  }
}

export default QueryRouter;
